﻿#ifndef QPCRMODEL_H
#define QPCRMODEL_H

#include <QVector>
#include <QPointF>
#include <cmath>
#include <QDebug>
#include <QMap>


#include "funcModel.h"
#include "funcExponent.h"
#include "funcLogistic.h"
#include "funcRichards.h"
#include "funcSigmoid.h"


/* 2024.08.05
 * 1. 修正原有的初始化偏差导致拟合不收敛的问题
 * 2. 拆分funcModel文件
 * 3. 增加Logistic函数、Exp函数、扩增效率E计算方式 (待验证，暂不开放)
 */

namespace QPCRModel {


class Model
{

public:


    enum YAxisType{
        RN = 0,
        DELTA_RN = 1,
        LOG_RN = 2,
        LOG_DELTA_RN = 3,
        NORM = 4,
    };


    enum ModelParam{
        FUNC_TYPE,          // 函数模型类型 0=Sigmoid 1=Richards 默认取1
        ITER_TIMES,         // 迭代次数

        EXP_LEN,            // 指数期长度
        BASE_LEN,           // 基线选取长度 范围[1, 10]  默认取3
        CLIP_START,         // 前N个循环视为无效数据
        SLOPE_THR,
        SLOPE_THR_DOWN,
        FIXED_BASE_END,
        MIN_CONF,
        RANGE_THR,

        // 准备弃用
        BASE_START,
        BASE_END        // 基线开始和结束的长度，仅用于判定阈值线位置

    };

public:
    Model();
    ~Model();


    void setRawData(QVector<QPointF> pointList);    // 步骤1 设置原始数据
    void setParam(ModelParam type, double value);   // 步骤2 设置函数参数（如果不设置，模型会使用默认配置进行拟合）
    void startCalculation();                        // 步骤3 开始拟合计算

    QMap<QString, double> getParam();        // 获取当前设置的参数
    void setDefaultParam();                  // 恢复默认参数设置（不包括函数模型）


    // 步骤4 获取拟合结果
    QVector<QPointF>  getRawData();
    QVector<QPointF>  getFittedData();            // Rn
    QVector<QPointF>  getFittedData_delta();      // ΔRn
    QVector<QPointF>  getFittedData_log();        // log(Rn)
    QVector<QPointF>  getFittedData_log_delta();  // log(ΔRn)
    QVector<QPointF>  getFittedData_norm();       // normlize(Rn) 归一化
    QVector<QPointF>  getFittedDate_diff(double range);       // normlize标准化

    bool            fitted();       // 返回函数是否拟合成功

    double          getSPE();
    double          getFDM();
    double          getSDM();


    double          getCt();                    // 默认阈值线得到的Ct
    double          getCt(double threshold, YAxisType type);    // 自定义阈值线得到的Ct
    double          getCt_relative(int type=0); // 相对阈值线Ct -  Type0: 0.5*(F(SDM)+F0) Type1:CSDM Type2:Cy0

    double          getBgValue();               // 基线值
    double          getTopValue(int type = 0);                  // 获取拟合后的最大值
    double          getThreshold(YAxisType type);
    double          getThreshold(int cycle, YAxisType);           // 一个没什么用的接口，给绝对阈值加点高度
    double          setThresholdRange(int start, int end);   // 设定绝对阈值的范围
    double          setThresholdRange();

    double          getEfficiency();
    double          getSlope();


    double          getFittingConf();           // 拟合结果可信度，范围在[0.9,1]之间表示拟合成功，-1或0表示拟合失败

    bool            finished();                 // 这个函数可能不好使

    QVector<double> getFuncParam();             // 获取函数模型的参数
                                                // param[0]: 函数类型
                                                // param[1]: 拟合可信度
                                                // param[2~n]: 函数模型参数


    static double           getCt(double threshold, QVector<double> param, YAxisType type);     // 没有函数模型时用于获取绝对Ct的方法
    static double           yingCTJiao(double *yingLvbo, double yuzhi, int end, int leh);
    static double           distancePL(QPointF pt, double k, double b);     // 点到直线距离，返回值>0代表点在直线上方，<0代表在下方
    static double           calDistance(double x1, double x2, double y1, double y2);     // 点到点
    static double           linearFitting(const QVector<QPointF>& points_list, double* k, double* b);    // 线性拟合
    static double           calCoeff(const QVector<QPointF>& points_list);
    static double           correlationPointList(const QVector<QPointF>& pointList);
    static QVector<QPointF>     filter(const QVector<QPointF> &list, int type=0);  // 滤波算法 - Type0: 三点均值 Type1: 五点二阶  （建议暂时不要用Type1）

    // （测试用的接口，没啥用）
    QVector<QPointF>    getBaselineData();
    void                printParam();
    void                printSlopeList();
    void                printLMLogs();


private:


    int             calculate();
    int             calBaseline();
    double          calEfficiency();
    int             findExpStage();     // 已弃用
    int             findExpStage2();
    int             LevenbergMarquardt(const QVector<QPointF>& dataList);

    int             updatePointList();
    int             updateParam();

private:

    int             _cycleNum;

    double          _RNoise;
    double          _CSPE;          // 指数期开始点
    double          _CSPL;          // 线性期开始点
    double          _CEPE;          // 指数期结束点
    double          _CSDM;
    double          _CFDM;

    double          _E;
    double          _slope;         // 后面可能扩展成类似LinReg的使用多组数据计算E值
    double          _Rmax;
    double          _Rmin;

    double          _threshold;     // 每条曲线的最低阈值

    double          _bgValue;       // 背景荧光 = 基线值
    double          _base_k;
    double          _base_b;

    /* 用户可改的一些参数 */
    // 模型参数
    int             _functype       = 1;            // 函数类型
    int             _iterationNum   = 100;
    // 指数期确定
    int             _expLen         = 5;            // 指数期算法判断长度
    int             _baseLen        = 3;            // 参与计算的基线保留长度
    int             _clipStart      = 5;            // 裁剪位置 - 前几个循环视为无效数据
    double          _slopeThr       = 2.8;          // 指数期查找 - 斜率限定范围
    double          _slopeDownThr   = 0.6;          // 指数期查找 - 是否存在下降区域
    double          _fixedBaseEnd   = -1;           // 固定基线结束的循环 与上面所有的筛选参数互斥
    // 后期筛选
    double          _minConf        = 0.9;          // 置信度在多少以下过滤
    double          _minRange       = 1000;         // 曲线扩增范围

    // 默认参数
    int             _iterationNum_default   = 100;
    int             _expLen_default         = 5;
    int             _baseLen_default        = 3;
    int             _clipStart_default      = 5;
    double          _slopeThr_default       = 2.8;
    double          _slopeDownThr_default   = 0.6;
    double          _fixedBaseEnd_default   = -1;
    double          _minConf_default        = 0.9;
    double          _minRange_default       = 1000;




    // 函数模型
    funcModel*      func;

    funcSigmoid*    _pSigmoid;
    funcRichards*   _pRichards;
    funcLogistic*   _pLogistic;
    funcExponent*   _pExponent;

    // 返回的数据点
    QVector<QPointF>  _rawPointList;
    QVector<QPointF>  _fittedPointList;
    QVector<QPointF>  _fittedPointList_delta;
    QVector<QPointF>  _fittedPointList_log;
    QVector<QPointF>  _fittedPointList_log_delta;
    QVector<QPointF>  _fittedPointList_norm;
    QVector<QPointF>  _baselinePointList;



    // 状态判断
    double          _fittingConf;
    bool            _FINISHED;
    bool            _S_SHAPED;

    // 调试用
    QVector<double>     _slopeList;
    QVector<QString>    _LM_logs;   // LM算法调试记录
    QString             _resState;  // 标一下拟合失败的原因



};

}






#endif // QPCRMODEL_H
